/*
        Copyright (C) 2007  Aaron Defazio

        This program is free software; you can redistribute it and/or
        modify it under the terms of the GNU General Public License
        as published by the Free Software Foundation; either version 2
        of the License, or (at your option) any later version.

        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;
using PluginInterface;
using System.Reflection;


namespace AppSearchPlugin
{
    abstract class AppSearch : IPlugin
    {

        protected DataStore GDataStore;
        protected int MaxReturned = 10;

        public AppSearch()
        {               
            //Initilise();
        }

        #region plugin Members

        public virtual string Name
        {
            get { return "System Applications index"; }
        }
        public virtual string Shortcut
        {
            get { return ""; }
        }

        public virtual void ReIndex()
        {
            if(GDataStore == null)
                GDataStore = new DataStore();
            //TODO: expand this so users can add aditional folders, maybe a xml config file or something
            string path = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);       //users start menu including user name in path
            AddFolder(GDataStore, path, true, "*.lnk");    //including child directories, add only link files
            path = @"C:\Documents and Settings\All Users\Start Menu";
            AddFolder(GDataStore, path, true, "*.lnk");

        }

        public virtual List<RObject> DoQuery(string query)
        {
            return GDataStore.Retrieve(query, MaxReturned);
        }

        public virtual void Open(string key, string QueryTerm)
        {
            GDataStore.IncreasePopularity(key);

            try
            {
                Process.Start(key);
            }
            catch
            {
                MessageBox.Show("Unable to start program");
            }
        }

        /// <summary>
        /// Due to a weird quirk/bug of binary serialization inside of dlls, I need to make sure that it 
        /// loads this assembly a second time, since after its loaded the first time the binary serializer
        /// can't pick it up. So this is called when it fails during serialization.
        /// </summary>
        Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            if (args.Name.StartsWith("AppSearchPlugin"))
                return Assembly.LoadFile(Path.GetFullPath("plugins\\AppSearchPlugin.dll"));
            else
                return null;
        }

        public virtual void Initilise()
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            if (File.Exists("appstore"))
            {
                try
                {
                    using (FileStream s = new FileStream("appstore", FileMode.Open))
                    {
                        BinaryFormatter b = new BinaryFormatter();
                        b.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
                        GDataStore = (DataStore)b.Deserialize(s);
                    }
                }
                catch( Exception e)
                {                    
                    MessageBox.Show("Error loading appstore serialized file!");
                    MessageBox.Show(e.Message);
                    GDataStore = new DataStore();
                    ReIndex();
                }

            }
            else
            {
                GDataStore = new DataStore();
                ReIndex();
            }
        }

        public virtual void close()
        {
            try
            {
                //Serialize the datastore and store in the application directory
                using (FileStream s = new FileStream("appstore", FileMode.Create))
                {
                    BinaryFormatter b = new BinaryFormatter();
                    b.Serialize(s, GDataStore);
                }
            }
            catch {
                MessageBox.Show("Error storing appstore");
            }

        }

        #endregion

        /// <summary>
        ///  Add the folder to the passed datastore
        /// </summary>
        /// <param name="ds">datastore</param>
        /// <param name="FolderPath">Full path of folder to add</param>
        /// <param name="childDirectories">Add all child Directories TRUE/FALSE </param>
        /// <param name="filter">File filter e.g. ".lnk" or ".exe" for links, executables respectively</param>
        protected virtual void AddFolder(DataStore ds, string FolderPath, bool childDirectories, string filter)
        {
            SearchOption cd;
            if (childDirectories)
                cd = SearchOption.AllDirectories;
            else
                cd = SearchOption.TopDirectoryOnly;

            //Get the full paths for each file
            string[] Files;
            try
            {
                Files = Directory.GetFiles(FolderPath, filter, cd);
            }
            catch( Exception e) {
                MessageBox.Show(e.Message);
                return;
            }

            foreach (string ff in Files)
            {                
                //Get the filename part of the full path
                string fn = Path.GetFileNameWithoutExtension(ff);
                //split into keywords
                string[] keywords = fn.Split(' ');

                int popularity = 1;     //TODO: add

                ds.add(ff, new RData(fn, keywords, popularity, GetIcon(ff)));
            }
        }



        /// <summary>
        /// Get the large icon for the associated file. Code borrowed from
        /// http://www.codeguru.com/csharp/csharp/cs_misc/icons/article.php/c4261/
        /// </summary>
        /// <param name="FileName">Full File path</param>
        /// <returns></returns>
        protected virtual System.Drawing.Icon GetIcon(string FileName)
        {
            IntPtr hImgLarge = IntPtr.Zero;    //the handle to the system image list
            SHFILEINFO shinfo = new SHFILEINFO();
            try
            {
                hImgLarge = Win32.SHGetFileInfo(FileName, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFI_ICON | Win32.SHGFI_LARGEICON);
            }
            catch
            {
                MessageBox.Show("Error retrieving icon for: " + FileName);               
            }

            //The icon is returned in the hIcon member of the shinfo
            //struct            
            if (hImgLarge != IntPtr.Zero)
                return System.Drawing.Icon.FromHandle(shinfo.hIcon);
            else
                return null;

        }

    }

    //Interop stuff taken from http://www.codeguru.com/csharp/csharp/cs_misc/icons/article.php/c4261/
    #region Unmanaged Icon Stuff

    [StructLayout(LayoutKind.Sequential)]
    public struct SHFILEINFO
    {
        public IntPtr hIcon;
        public IntPtr iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    };

    class Win32
    {
        public const uint SHGFI_ICON = 0x100;
        public const uint SHGFI_LARGEICON = 0x0;    // 'Large icon
        public const uint SHGFI_SMALLICON = 0x1;    // 'Small icon

        [DllImport("shell32.dll")]
        public static extern IntPtr SHGetFileInfo(string pszPath,
                                    uint dwFileAttributes,
                                    ref SHFILEINFO psfi,
                                    uint cbSizeFileInfo,
                                    uint uFlags);
    }

    #endregion

}
